package future;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.thread.ThreadUtil;
import com.google.common.collect.Lists;
import threadpool.ThreadPoolTest;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * @description: CompletableFuture使用demo
 * @author: kanggw
 * @date: 2022/11/30
 **/
public class CompletableFutureDemo {

    public void commonTest() {
        ThreadPoolExecutor threadPool = ThreadPoolTest.getThreadPool();

        List<String> strings = Lists.newArrayList("1", "2", "3");

        strings.forEach(item -> {

            CompletableFuture.supplyAsync(() -> out(item), threadPool).whenCompleteAsync((result,eror) -> {
                System.out.println(result);
            });

//            completableFuture.
        });

        try {
            Thread.sleep(1000*20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        threadPool.shutdown();

    }

    public static void commonTestTwo() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<Void> future = new CompletableFuture();
        future.get(5, TimeUnit.MINUTES);
    }

    public String out(String s){
        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return s;
    }

    /**
     * 创建CompletionStage
     */
    public static void createCompletionStage() {
        CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread() +""+ System.currentTimeMillis());
            return System.currentTimeMillis();
        }, ThreadUtil.newExecutor());

        try {
            Long aLong = future.get(1100,TimeUnit.MILLISECONDS);
            System.out.println(Thread.currentThread() +""+ aLong);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * completionStage异常处理
     */
    public static void completionStageError() {

        CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + "" + System.currentTimeMillis());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("sleep过程中被中断了，会进入此异常");
                ;
            }
            throw new RuntimeException("模拟异常");
        }, ThreadUtil.newExecutor());
        //处理完成后会进入这个方法，包括有异常的情况
        future.whenComplete((aLong, throwable) -> {
            System.out.println("whenComplete 方法");
            System.out.println("aLong:" + aLong);
            System.out.println(throwable.getMessage());
        });
        //如果有异常就会进入这个方法
        future.exceptionally(t -> {
            System.out.println("exception 处理" + t.getMessage());
            return 1L;
        });

        try {
            Long aLong = future.get(1500, TimeUnit.MILLISECONDS);
            System.out.println(aLong);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * Handle方法使用
     */
    public static void completionHandleMethod() {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            ThreadUtil.sleep(1000);
//            throw new RuntimeException("模拟异常");
        }, ThreadUtil.newExecutor());

        future.handle((value, throwable) -> {
            if (null != throwable) {
                System.out.println("抛异常了:" + throwable.getMessage());
            } else {
                System.out.println("正常执行了");
            }
            return null;
        });

        try {
            System.out.println("获取处理结果");
            Void unused = future.get(1500, TimeUnit.MILLISECONDS);
            System.out.println(unused);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 适用于两个串行的异步任务
     * 计算 （10 + 10 ）* 20
     */
    public static void thenApplyMethod() {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 10 + 10;
        }, ThreadUtil.newExecutor());
        CompletableFuture<Integer> future2 = future.thenApplyAsync(first -> first * 20, ThreadUtil.newExecutor());
        try {
//            CompletableFuture.allOf(future, future2).join();
            Integer integer = future.get();
            Integer integer2 = future2.get();
            System.out.println("out:" + integer);
            System.out.println("out:" + integer2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 适用于两个串行的异步任务,与thenApply的区别是，第二个异步任务返回的是一个CompletionStage实例
     * 计算 （10 + 10 ）* 20
     */
    public static void thenComposeMethod() {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 10 + 10;
        }, ThreadUtil.newExecutor());
        CompletableFuture<Integer> future2 = future.thenComposeAsync(first -> CompletableFuture.supplyAsync(() -> first * 20), ThreadUtil.newExecutor());
        try {
//            CompletableFuture.allOf(future, future2).join();
            Integer integer = future.get();
            Integer integer2 = future2.get();
            System.out.println("out:" + integer);
            System.out.println("out:" + integer2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 第三个任务需要依赖前2个任务的场景
     * 计算  （1+1） * （2+2）
     */
    public static void thenCombineMethod() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1 + 1, ThreadUtil.newExecutor());
        CompletableFuture<Integer> future3 = future1.thenCombineAsync(CompletableFuture.supplyAsync(() -> 2 + 2,ThreadUtil.newExecutor()),
                                                                     (v1, v2) -> v1 * v2,
                                                                     ThreadUtil.newExecutor());
        try {
            Integer integer = future3.get();
            System.out.println("out:" + integer);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    public static void thenAllOfMethod() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 1);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            ThreadUtil.sleep(2, TimeUnit.SECONDS);
            return 2;
        });
        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> 3);
        CompletableFuture<Integer> future4 = CompletableFuture.supplyAsync(() -> 4);
        try {
            CompletableFuture.allOf(future1, future2, future3, future4).exceptionally(t -> null).get(1,TimeUnit.SECONDS);
            System.out.println(future1.get());
            System.out.println(future2.get());
            System.out.println(future3.get());
            System.out.println(future4.get());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {

//        commonTestTwo();

//        createCompletionStage();
//        completionStageError();
//        completionHandleMethod();
//        thenApplyMethod();
//        thenComposeMethod();
//        thenCombineMethod();
//        thenAllOfMethod();
//        caculationMoney();
//        System.out.println("======================");
//        caculationMoney2();
//        getInterResult();
        main2();
    }

    public static void caculationMoney() {
        BigDecimal init = new BigDecimal(22860);

        System.out.println("基本保险金额：" + init);
        BigDecimal total = BigDecimal.ZERO;
        BigDecimal yearTotal = BigDecimal.ZERO;
        for (int i = 1; i <= 20; i++) {
            if (i > 1) {
                yearTotal = yearTotal.add(yearTotal.multiply(new BigDecimal("0.03"))).setScale(2, RoundingMode.HALF_UP);
            } else {
                yearTotal = init;
            }
            System.out.println("第" + i +"年度领取金额为：" + yearTotal);
            total = total.add(yearTotal).setScale(2, RoundingMode.HALF_UP);;
            System.out.println("总领取金额：" + total);
        }

    }

    public static void caculationMoney2() {

        BigDecimal init = new BigDecimal(10000);
        BigDecimal initXiaoFei = new BigDecimal(22860);
        BigDecimal yearTotal = BigDecimal.ZERO;
        BigDecimal lv = new BigDecimal("0.03");
        for (int i = 1; i <= 50; i++) {
            if (i == 1) {
                yearTotal = init.add(init.multiply(lv).setScale(2, RoundingMode.HALF_UP));
            } else if (i <= 30){
                yearTotal = yearTotal.add(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP);
                yearTotal = yearTotal.add(yearTotal.multiply(lv ).setScale(2, RoundingMode.HALF_UP));
            }else {
                yearTotal = yearTotal.add(yearTotal.multiply(lv).setScale(2, RoundingMode.HALF_UP));
                initXiaoFei = getXiaoFei(initXiaoFei);
                System.out.println("第" + (i - 30) + "年领取"+initXiaoFei);
                yearTotal = yearTotal.subtract(initXiaoFei).setScale(2, RoundingMode.HALF_UP);
                System.out.println("第" + (i - 30) + "年剩余"+yearTotal);
            }
        }
    }

    public static BigDecimal getXiaoFei(BigDecimal init) {
        BigDecimal yearTotal = BigDecimal.ZERO;
        yearTotal = init.add(init.multiply(new BigDecimal("0.03"))).setScale(2, RoundingMode.HALF_UP);
        return yearTotal;
    }

    public static void main2() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return 1;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(550);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return 2;
        });

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return 3;
        });

        ArrayList<CompletableFuture<Integer>> completableFutures = Lists.newArrayList(future1, future2, future3);
        //        List<Object> list = completableFutures.parallelStream()
        //                                              .map(future -> {
        //                                                  try {
        //                                                      //                                                      Thread.sleep(400);
        //                                                      return future.get(500, TimeUnit.MILLISECONDS);
        //                                                  } catch (InterruptedException | ExecutionException | TimeoutException e) {
        //                                                      return null;
        //                                                  }
        //                                              })
        //                                              .collect(Collectors.toList());
        //        System.out.println("out:" + list);

        //        try {
        //            CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]))
        //                             .get(500, TimeUnit.MILLISECONDS);
        //        } catch (InterruptedException | ExecutionException | TimeoutException e) {
        //            System.out.println("发生超时");
        //
        //        }
//        List<Object> list = completableFutures.stream()
//                                              .map(future -> {
//                                                  try {
//                                                      //                                                      Thread.sleep(400);
//                                                      return future.get(500, TimeUnit.MILLISECONDS);
//                                                  } catch (InterruptedException | ExecutionException | TimeoutException e) {
//                                                      return null;
//                                                  }
//                                              })
//                                              .collect(Collectors.toList())
//                                              .stream()
//                                              .filter(Objects::nonNull)
//                                              .collect(Collectors.toList());
        try {
            CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(500,TimeUnit.MILLISECONDS);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
            System.out.println("异常");
        }

        List<Integer> integers = completableFutures.stream()
                                                   .map(item -> {
                                                       try {
                                                           return item.get(500,TimeUnit.MILLISECONDS);
                                                       } catch (InterruptedException | ExecutionException | TimeoutException e) {
                                                           System.out.println("异常");
                                                           return null;
                                                       }
                                                   })
                                                   .collect(Collectors.toList());

        System.out.println("out:" + integers);

        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());

    }

    private static void getInterResult() {
        ArrayList<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5);
        StopWatch stopWatch = StopWatch.create("1");
        stopWatch.start();
        List<Integer> integerList = integers.stream()
                                        .map(param -> getResult(param))
                                        .map(item -> item.join())
                                        .collect(Collectors.toList());
        stopWatch.stop();
        System.out.println(integerList);
        System.out.println(stopWatch.getTotalTimeMillis());
    }
    private static CompletableFuture<Integer> getResult(Integer param) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(param * 100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return param;
        },ThreadUtil.newExecutor());
    }

}
//100 + 200 + 300 + 400 + 500